package frontend.visitor;

import frontend.lexer.ErrorCode;
import frontend.lexer.GramError;
import frontend.lexer.TKType;
import frontend.parser.syntaxtree.*;
import frontend.symtable.SymbolTable;
import frontend.symtable.symbol.FunctionSymbol;
import frontend.symtable.symbol.Type;
import frontend.symtable.symbol.VarSymbol;
import middleware.llvm.BasicBlock;
import middleware.llvm.Module;
import middleware.llvm.enums.CmpType;
import middleware.llvm.instruction.AllocaInstruction;
import middleware.llvm.instruction.BrInstruction;
import middleware.llvm.instruction.ICmpInstruction;
import middleware.llvm.value.FunctionIRType;
import middleware.llvm.value.IRType;
import middleware.llvm.value.ImmediateValue;
import middleware.llvm.value.Value;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Stack;

public class Visitor {
    private final Module irModule = new Module();//生成的IR模块
    private FunctionIRType currentFunction = null;//正在处理的函数
    private BasicBlock currentBasicBlock = null;//正在处理的基本块

    // for break and continue
    private final Stack<List<BrInstruction>> continueBranches = new Stack<>();
    private final Stack<List<BrInstruction>> breakBranches = new Stack<>();

    private final ArrayList<GramError> errorList;
    private final SymbolTable symbolTable = new SymbolTable();


    private SymbolTable currentSymbolTable = symbolTable;
    private int loopDepth = 0;
    private boolean isReturnExpressionNotNeeded = false;
    private boolean isGlobalVar = true;

    public Visitor(ArrayList<GramError> errorList) {
        this.errorList = errorList
        ;
    }

    public SymbolTable getCurrentSymbolTable() {
        return currentSymbolTable;
    }

    public void setCurrentSymbolTable(SymbolTable currentSymbolTable) {
        this.currentSymbolTable = currentSymbolTable;
    }

    public VisitResult visitAddExpNodeForDouble(AddExpNodeForDouble elm) {
        var rt = new VisitResult();
        var r1 = visitAddExpNode(elm.addExp);
        var r2 = visitMulExpNode(elm.mulExp);
        var val1 = r1.constVal;
        var val2 = r2.constVal;

        assert r1.expressionType.equals(r2.expressionType);
        rt.expressionType = r1.expressionType;
        if (val1 != null && val2 != null) {
            if (elm.op == TKType.PLUS) {
                rt.constVal = val1 + val2;
            } else {
                rt.constVal = val1 - val2;
            }
        }
        if (currentBasicBlock != null) {
            if (elm.op == TKType.PLUS) {
                rt.irValue = currentBasicBlock.createAddInst(r1.irValue, r2.irValue);
            } else {
                rt.irValue = currentBasicBlock.createSubInst(r1.irValue, r2.irValue);
            }
        }
        return rt;
    }

    public VisitResult visitAddExpNodeForSingle(AddExpNodeForSingle elm) {
        return visitMulExpNode(elm.mulExp);
    }

    public VisitResult visitAddExpNode(AddExpNode elm) {
        if (elm instanceof AddExpNodeForSingle) {
            return visitAddExpNodeForSingle((AddExpNodeForSingle) elm);
        } else {
            return visitAddExpNodeForDouble((AddExpNodeForDouble) elm);
        }
    }

    public void visitBlockItemNodeForDecl(BlockItemNodeForDecl elm) {
        visitDeclNode(elm.decl);
    }

    public void visitBlockItemNodeForStmt(BlockItemNodeForStmt elm) {
        visitStmtNode(elm.stmt);
    }

    public void visitBlockItemNode(BlockItemNode elm) {
        if (elm instanceof BlockItemNodeForDecl) {
            visitBlockItemNodeForDecl((BlockItemNodeForDecl) elm);
        } else {
            visitBlockItemNodeForStmt((BlockItemNodeForStmt) elm);
        }
    }

    public void visitBlockNode(BlockNode elm) {
//        for (var blockItem : elm.blockItems) {
//            visitBlockItemNode(blockItem);
//        }
        elm.blockItems.forEach(this::visitBlockItemNode);
    }

    public String visitBTypeNode(BTypeNode elm) {
        return elm.getType();
    }

    public Module generateIR(SyntaxNode root) {
        if (root instanceof CompUnitNode compNode) {
            visitCompUnitNode(compNode);
            return irModule;
        } else {
            return null;
        }
    }

    public void visitCompUnitNode(CompUnitNode elm) {
        isGlobalVar = true;
//        for (var declare : elm.declares) {
//            visitDeclNode(declare);
//        }
        elm.declares.forEach(this::visitDeclNode);
        isGlobalVar = false;

//        for (var func : elm.functions) {
//            visitFuncDefNode(func);
//        }
        elm.functions.forEach((this::visitFuncDefNode));

        visitMainFuncDefNode(elm.mainFunc);
    }

    public VisitResult visitCondNode(CondNode elm) {
        return visitLOrExpNode(elm.lOrExp);
    }

    public void visitConstDeclNode(ConstDeclNode elm) {
//        for (var constDef : elm.constDefs) {
//            visitConstDefNode(elm.type, constDef);
//        }
        elm.constDefs.forEach(constDef -> visitConstDefNode(elm.type, constDef));
    }

    public void visitConstDefNode(BTypeNode BType, ConstDefNode elm) {
        if (currentSymbolTable.contains(elm.ident)) {//当前作用域内部不存在
            errorList.add(new GramError(ErrorCode.b, elm.identLineNum));
        }
        var varSym = new VarSymbol();
        varSym.ident = elm.ident;
        varSym.isConst = true;
        varSym.varType.type = BType.type.getTktype();
        elm.dimensions.forEach(dimension -> varSym.varType.dims.add(visitConstExpNode(dimension).constVal));

        var r = visitConstInitValNode(elm.constInitVal);
        varSym.values.addAll(r.constantInitValues);
        var initValues = r.irValues;
        while (!varSym.varType.dims.isEmpty() && varSym.values.size() < varSym.varType.dims.get(0)) {
            varSym.values.add(0);
        } //补全0

        if (isGlobalVar) {
//        if (true) {
            var globalVar = irModule.createGlobalValue(IRType.getInt().dims(varSym.varType.dims), varSym.values);
            globalVar.setName(varSym.ident);
            varSym.targetValue = globalVar;
        } else {
            //localvalue
            varSym.targetValue = currentFunction.getFirstBasicBlock().createAllocaInstAndInsertToFront(IRType.getInt().dims(varSym.varType.dims));
            if (!varSym.isArray()) {
                currentBasicBlock.createStoreInst(initValues.get(0), varSym.targetValue);
            } else { // array with init values
                for (int i = 0; i < initValues.size(); i++) {
                    int[] idxs = new int[varSym.varType.dims.size()];
                    var pos = i;
                    for (int j = idxs.length - 1; j >= 0; j--) {
                        idxs[j] = pos % varSym.varType.dims.get(j);
                        pos /= varSym.varType.dims.get(j);
                    }

                    var initVal = initValues.get(i);
                    var arrayPtr = currentBasicBlock.createGetElementPtrInst(varSym.targetValue, List.of(new ImmediateValue(0), new ImmediateValue(0)));
                    for (int j = 0; j < idxs.length; j++) {
                        var visitIdx = idxs[j];
                        List<Value> offsets = j == idxs.length - 1 ? List.of(new ImmediateValue(visitIdx)) : List.of(new ImmediateValue(visitIdx), new ImmediateValue(0));
                        arrayPtr = currentBasicBlock.createGetElementPtrInst(arrayPtr, offsets);
                    }
                    currentBasicBlock.createStoreInst(initVal, arrayPtr);
                }
            }
        }

        currentSymbolTable.insertSymbol(varSym);
    }

    public VisitResult visitConstExpNode(ConstExpNode elm) {
        var rt = visitAddExpNode(elm.addExp);
        assert rt.constVal != null;
        return rt;
    }

    public VisitResult visitConstInitValNodeForArrayInit(ConstInitValNodeForArrayInit elm) {
        var rt = new VisitResult();
//        for (var init : elm.initValues) {
//            var r = visitConstExpNode(init);
//            rt.constantInitValues.add(r.constVal);
//            rt.irValues.add(r.irValue);
//        }
        elm.initValues.forEach(init -> {
            var r = visitConstExpNode(init);
            rt.constantInitValues.add(r.constVal);
            rt.irValues.add(r.irValue);
        });
        return rt;
    }

    public VisitResult visitConstInitValNodeForConstExp(ConstInitValNodeForConstExp elm) {
        var rt = new VisitResult();
        var r = visitConstExpNode(elm.constExp);
        rt.constantInitValues.add(r.constVal);
        rt.irValues.add(r.irValue);
        return rt;
    }

    public VisitResult visitConstInitValNode(ConstInitValNode elm) {
        if (elm instanceof ConstInitValNodeForArrayInit) {
            return visitConstInitValNodeForArrayInit((ConstInitValNodeForArrayInit) elm);
        } else if (elm instanceof ConstInitValNodeForStringCon) {
            return visitConstInitValNodeForStringCon((ConstInitValNodeForStringCon) elm);
        } else {
            return visitConstInitValNodeForConstExp((ConstInitValNodeForConstExp) elm);
        }
    }

    public void visitDeclNodeForConstDecl(DeclNodeForConstDecl elm) {
        visitConstDeclNode(elm.constDecl);
    }

    public void visitDeclNodeForVarDecl(DeclNodeForVarDecl elm) {
        visitVarDeclNode(elm.varDecl);
    }

    public void visitDeclNode(DeclNode elm) {
        if (elm instanceof DeclNodeForVarDecl) {
            visitDeclNodeForVarDecl((DeclNodeForVarDecl) elm);
        } else {
            visitDeclNodeForConstDecl((DeclNodeForConstDecl) elm);
        }
    }

    public VisitResult visitEqExpNodeForDouble(EqExpNodeForDouble elm) {
        var rt = new VisitResult();
        var r1 = visitEqExpNode(elm.eqExp);
        var r2 = visitRelExpNode(elm.relExp);

        if (r1.irValue instanceof ICmpInstruction) {
            r1.irValue = currentBasicBlock.createZExtInst(IRType.getInt(), r1.irValue);
        }
        if (r2.irValue instanceof ICmpInstruction) {
            r2.irValue = currentBasicBlock.createZExtInst(IRType.getInt(), r2.irValue);
        }

        CmpType cond = elm.op == TKType.EQL ? CmpType.EQ : CmpType.NEQ;
        rt.irValue = currentBasicBlock.createICmpInst(cond, r1.irValue, r2.irValue);
        return rt;
    }

    public VisitResult visitEqExpNodeForSingle(EqExpNodeForSingle elm) {
        return visitRelExpNode(elm.relExp);
    }

    public VisitResult visitEqExpNode(EqExpNode elm) {
        if (elm instanceof EqExpNodeForDouble) {
            return visitEqExpNodeForDouble((EqExpNodeForDouble) elm);
        } else {
            return visitEqExpNodeForSingle((EqExpNodeForSingle) elm);
        }
    }

    public VisitResult visitExpNode(ExpNode elm) {
        return visitAddExpNode(elm.addExp);
    }

    public void visitForStmtNode(ForStmtNode elm) {
        var lValSym = currentSymbolTable.getSymbol(elm.lVal.ident);
        if (lValSym == null) return;
        if (lValSym instanceof VarSymbol lValVarSym && lValVarSym.isConst) {
            errorList.add(new GramError(ErrorCode.h, elm.lVal.identLineNum));
        }
        var r1 = visitLValNode(elm.lVal);
        var r2 = visitExpNode(elm.exp);

        currentBasicBlock.createStoreInst(r2.irValue, r1.irValue);
    }

    public void visitFuncDefNode(FuncDefNode elm) {
        if (currentSymbolTable.contains(elm.ident)) {
            errorList.add(new GramError(ErrorCode.b, elm.identLineNum));
            return;
        }

        var sym = new FunctionSymbol();
        sym.ident = elm.ident;

        sym.retType = visitFuncTypeNode(elm.funcType);

        currentSymbolTable = currentSymbolTable.createSubTable();
        if (elm.params != null) {
            sym.paramTypeList.addAll(visitFuncFParamsNode(elm.params));
        }

        currentSymbolTable.getPreTable().insertSymbol(sym);

        isReturnExpressionNotNeeded = sym.retType.type.equals("VOIDTK");

        ArrayList<IRType> irArgTypes = new ArrayList<>();
        for (var type : sym.paramTypeList) {
            if (type.dims.isEmpty()) {
                irArgTypes.add(IRType.getInt());
            } else {
                irArgTypes.add(IRType.getInt().dims(type.dims.subList(1, type.dims.size())).ptr(1));
            }
        }
        currentFunction = irModule.createFunction(sym.retType.type.equals("VOIDTK") ? IRType.getVoid() : IRType.getInt(), irArgTypes);
        currentFunction.setName(sym.ident);
        sym.targetValue = currentFunction;
        currentBasicBlock = currentFunction.createBasicBlock();
        currentBasicBlock.setLoopNum(loopDepth);

        if (elm.params != null) {
            for (int i = currentFunction.getArguments().size() - 1; i >= 0; i--) { // the order of alloca for args is reversed to fit mips
                var currArgVal = currentFunction.getArguments().get(i);
                var currParamSym = currentSymbolTable.getSymbol(elm.params.params.get(i).ident);
                if (currParamSym == null) return;
                var currArgPtr = currentFunction.getFirstBasicBlock().createAllocaInstAndInsertToFront(currArgVal.getType());
                currentBasicBlock.createStoreInst(currArgVal, currArgPtr);
                currParamSym.targetValue = currArgPtr;
            }
        }

        visitBlockNode(elm.block);

        if (sym.retType.type.equals("VOIDTK") && elm.block.isWithoutReturn()) {
            currentBasicBlock.createReturnInst(null);
        }

        currentBasicBlock = null;
        currentFunction = null;

        if (!sym.retType.type.equals("VOIDTK") && elm.block.isWithoutReturn()) {
            errorList.add(new GramError(ErrorCode.g, elm.block.blockRLineNum));
        }

        currentSymbolTable = currentSymbolTable.getPreTable();
    }

    /**
     * @param elm FuncFParamNode函数形参
     * @return {@code Type}
     */
    public Type visitFuncFParamNode(FuncFParamNode elm) {
        if (currentSymbolTable.contains(elm.ident)) {
            errorList.add(new GramError(ErrorCode.b, elm.identLineNum));
        }

        var varSym = new VarSymbol(elm.ident, false);
//        varSym.ident = elm.ident;
//        varSym.isConst = false;
        varSym.varType.type = visitBTypeNode(elm.type);//"FuncFParam"
        varSym.varType.type = elm.type.type.getTktype();
        if (elm.dimensions != null) {
            varSym.varType.dims.add(null); // for dim 0
//            for (var dim : elm.dimensions) {
//                varSym.varType.dims.add(visitConstExpNode(dim).constVal);
//            }
            elm.dimensions.forEach(dim -> varSym.varType.dims.add(visitConstExpNode(dim).constVal));
        }
        currentSymbolTable.insertSymbol(varSym);
//        System.err.println(currentSymbolTable+"\n");
        var rt = new Type();
        rt.type = elm.type.type.getTktype();//FuncFParamNode.BTypeNode.TKType.getTKType()
        rt.dims.addAll(varSym.varType.dims);
        return rt;
    }

    public ArrayList<Type> visitFuncFParamsNode(FuncFParamsNode elm) {
        var rt = new ArrayList<Type>();
//        for (var param : elm.params) {
//            var paramType = visitFuncFParamNode(param);
//            if (paramType != null) {
//                rt.add(paramType);
//            }
//        }
        elm.params.forEach(param -> {
            var paramType = visitFuncFParamNode(param);
            if (paramType != null) {
                rt.add(paramType);
            }
        });
        return rt;
    }

    public VisitResult visitFuncRParamsNode(FuncRParamsNode elm) {
        var rt = new VisitResult();
//        for (var exp : elm.exps) {
//            var r = visitExpNode(exp);
//            rt.parameterTypes.add(r.expressionType);
//            rt.irValues.add(r.irValue);
//        }
        elm.exps.forEach(exp -> {
            var r = visitExpNode(exp);
            rt.parameterTypes.add(r.expressionType);
            rt.irValues.add(r.irValue);
        });
        return rt;
    }

    public Type visitFuncTypeNode(FuncTypeNode elm) {
        var rt = new Type();
        rt.type = elm.type.toString();
        return rt;
    }

    public VisitResult visitInitValNodeForArray(InitValNodeForArray elm) {
        var rt = new VisitResult();
//        for (var init : elm.initVals) {
//            var r = visitExpNode(init);
//            rt.constantInitValues.add(r.constVal);
//            rt.irValues.add(r.irValue);
//        }
        elm.initVals.forEach(init -> {
            var r = visitExpNode(init);
            rt.constantInitValues.add(r.constVal);
            rt.irValues.add(r.irValue);
        });
        return rt;
    }

    public VisitResult visitInitValNodeForString(InitValNodeForString elm) {
        var rt = new VisitResult();

        for (char init : elm.string.substring(1, elm.string.length() - 1).toCharArray()) { // 去掉首尾 "
            rt.irValues.add(new ImmediateValue(init));
            rt.constantInitValues.add((int) init);
            rt.expressionType.type = "CHARTK";
        }
        return rt;
    }

    public VisitResult visitConstInitValNodeForStringCon(ConstInitValNodeForStringCon elm) {
        var rt = new VisitResult();
        for (var init : elm.stringConst.substring(1, elm.stringConst.length() - 1).toCharArray()) {//去掉首尾 "
            rt.irValues.add(new ImmediateValue(init));
            rt.constantInitValues.add((int) init);
            rt.expressionType.type = "CHARTK";
        }
        return rt;
    }


    public VisitResult visitInitValNodeForExp(InitValNodeForExp elm) {
        var rt = new VisitResult();
        var r = visitExpNode(elm.exp);
        rt.constantInitValues.add(r.constVal);
        rt.irValues.add(r.irValue);
        return rt;
    }

    public VisitResult visitInitValNode(InitValNode elm) {
        if (elm instanceof InitValNodeForArray) {
            return visitInitValNodeForArray((InitValNodeForArray) elm);
        } else if (elm instanceof InitValNodeForString) {
            return visitInitValNodeForString((InitValNodeForString) elm);
        } else {
            return visitInitValNodeForExp((InitValNodeForExp) elm);
        }
    }

    public VisitResult visitLAndExpNodeForDouble(LAndExpNodeForDouble elm) {
        var rt = new VisitResult();

        var r1 = visitLAndExpNode(elm.lAndExp);
        var lastAndBlock = r1.andBasicBlocks.get(r1.andBasicBlocks.size() - 1);
        var brInLastAndBlock = (BrInstruction) lastAndBlock.getInstructions().get(lastAndBlock.getInstructions().size() - 1);
        brInLastAndBlock.setTrueBranch(currentBasicBlock);
        rt.andBasicBlocks.addAll(r1.andBasicBlocks);

        var r2 = visitEqExpNode(elm.eqExp);
        if (!(r2.irValue instanceof ICmpInstruction)) {
            r2.irValue = currentBasicBlock.createICmpInst(CmpType.NEQ, new ImmediateValue(0), r2.irValue);
        }
        currentBasicBlock.createBrInstWithCond(r2.irValue, null, null);
        rt.andBasicBlocks.add(currentBasicBlock);
        currentBasicBlock = currentFunction.createBasicBlock();
        currentBasicBlock.setLoopNum(loopDepth);

        return rt;
    }

    public VisitResult visitLAndExpNodeForSingle(LAndExpNodeForSingle elm) {
        var rt = new VisitResult();

        var r = visitEqExpNode(elm.eqExp);
        if (!(r.irValue instanceof ICmpInstruction)) {
            r.irValue = currentBasicBlock.createICmpInst(CmpType.NEQ, new ImmediateValue(0), r.irValue);
        }
        currentBasicBlock.createBrInstWithCond(r.irValue, null, null);
        rt.andBasicBlocks.add(currentBasicBlock);
        currentBasicBlock = currentFunction.createBasicBlock();
        currentBasicBlock.setLoopNum(loopDepth);

        return rt;
    }

    public VisitResult visitLAndExpNode(LAndExpNode elm) {
        if (elm instanceof LAndExpNodeForDouble) {
            return visitLAndExpNodeForDouble((LAndExpNodeForDouble) elm);
        } else {
            return visitLAndExpNodeForSingle((LAndExpNodeForSingle) elm);
        }
    }

    public VisitResult visitLOrExpNodeForDouble(LOrExpNodeForDouble elm) {
        var rt = new VisitResult();

        var r1 = visitLOrExpNode(elm.lOrExp);
        rt.trueConditionBlocks.addAll(r1.trueConditionBlocks);
        var r2 = visitLAndExpNode(elm.lAndExp);
        rt.trueConditionBlocks.add(r2.andBasicBlocks.get(r2.andBasicBlocks.size() - 1));
        var firstAndBlock = r2.andBasicBlocks.get(0);
        for (var nearAndBlock : r1.nearAndBasicBlocks) {
            var brInst = (BrInstruction) nearAndBlock.getInstructions().get(nearAndBlock.getInstructions().size() - 1);
            brInst.setFalseBranch(firstAndBlock);
        }
        rt.nearAndBasicBlocks.addAll(r2.andBasicBlocks);
        rt.falseConditionBlocks.addAll(rt.nearAndBasicBlocks);

        return rt;
    }

    public VisitResult visitLOrExpNodeForSingle(LOrExpNodeForSingle elm) {
        var rt = new VisitResult();

        var r = visitLAndExpNode(elm.lAndExp);
        rt.trueConditionBlocks.add(r.andBasicBlocks.get(r.andBasicBlocks.size() - 1));
        rt.nearAndBasicBlocks.addAll(r.andBasicBlocks);
        rt.falseConditionBlocks.addAll(rt.nearAndBasicBlocks);

        return rt;
    }

    public VisitResult visitLOrExpNode(LOrExpNode elm) {
        if (elm instanceof LOrExpNodeForDouble) {
            return visitLOrExpNodeForDouble((LOrExpNodeForDouble) elm);
        } else {
            return visitLOrExpNodeForSingle((LOrExpNodeForSingle) elm);
        }
    }

    public VisitResult visitLValNode(LValNode elm) {
        var rt = new VisitResult();

        var sym = currentSymbolTable.getSymbol(elm.ident);
        if (sym == null) {
            errorList.add(new GramError(ErrorCode.c, elm.identLineNum));
            rt.expressionType.type = "ERR";//报错
            rt.irValue = new ImmediateValue(0);
            rt.constVal = 0;
            rt.irValues = List.of(new ImmediateValue(0));
            rt.constantInitValues = List.of(0);
            rt.parameterTypes = List.of(new Type());
            return rt;
        }
        var varSym = (VarSymbol) sym;

        List<Integer> accessDims = new ArrayList<>();
        List<Value> irVisitDims = new ArrayList<>();
        for (var dim : elm.dimensions) {
            var rtExp = visitExpNode(dim);
            accessDims.add(rtExp.constVal);//存储数组访问的维度值。
            irVisitDims.add(rtExp.irValue);//存储 IR 访问的维度值。
        }

        List<Integer> typeDims = new ArrayList<>();
        for (int i = accessDims.size(); i < varSym.varType.dims.size(); i++) {
            typeDims.add(varSym.varType.dims.get(i));
        }
        if (!typeDims.isEmpty()) {
            typeDims.set(0, null);
        }

        Type type = new Type();
        type.type = varSym.varType.type;
        type.dims.addAll(typeDims);
        rt.expressionType = type;
        if (varSym.isConst) {//const
            if (!varSym.isArray()) {//是变量
                rt.constVal = varSym.values.get(0);
            } else {//是数组
                int accessIdx = 0;
                int stride = 1;
                boolean validConst = true;
                for (int i = accessDims.size() - 1, j = varSym.varType.dims.size() - 1; i >= 0; i--, j--) {
                    var accessDim = accessDims.get(i);
                    if (accessDim == null) {
                        validConst = false;
                        break;
                    }
                    accessIdx += accessDim * stride;
                    stride *= varSym.varType.dims.get(j);
                }
                if (validConst) {
                    if (accessIdx >= varSym.values.size()) {//未指定部分初始化为0
                        rt.constVal = 0;
                    } else {
                        rt.constVal = varSym.values.get(accessIdx);
                    }
                }
            }
        }

        if (currentBasicBlock != null) {
            if (varSym.isArray()) {
                var dims = varSym.varType.dims;

                Value arrayPtr;
                Value symValue = varSym.targetValue;
                if (symValue instanceof AllocaInstruction allcaSymVal && allcaSymVal.getDataType().getPtrNum() != 0) {
                    arrayPtr = currentBasicBlock.createLoadInst(symValue);
                } else {
                    arrayPtr = currentBasicBlock.createGetElementPtrInst(symValue, List.of(new ImmediateValue(0), new ImmediateValue(0)));
                }

                for (int i = 0; i < irVisitDims.size(); i++) {
                    var visitDim = irVisitDims.get(i);
                    var offsets = (i == dims.size() - 1) ? List.of(visitDim) : List.of(visitDim, new ImmediateValue(0));
                    arrayPtr = currentBasicBlock.createGetElementPtrInst(arrayPtr, offsets);
                }

                rt.irValue = arrayPtr;
                rt.lvalLoadNotNeed = dims.size() != irVisitDims.size();
            } else {
                if (varSym.isConst) {
                    rt.irValue = new ImmediateValue(varSym.values.get(0));
                    rt.lvalLoadNotNeed = true;
                } else {
                    rt.irValue = varSym.targetValue;
                }
            }
        }

        return rt;
    }

    public void visitMainFuncDefNode(MainFuncDefNode elm) {
        var sym = new FunctionSymbol();
        sym.retType.type = "INTTK";
        sym.ident = "main";
//        currentSymbolTable.insertSymbol(sym);

        currentSymbolTable = currentSymbolTable.createSubTable();

        isReturnExpressionNotNeeded = false;

        ArrayList<IRType> irArgTypes = new ArrayList<>();
        currentFunction = irModule.createFunction(IRType.getInt(), irArgTypes);
        currentFunction.setName("main");
        sym.targetValue = currentFunction;
        currentBasicBlock = currentFunction.createBasicBlock();
        currentBasicBlock.setLoopNum(loopDepth);

        visitBlockNode(elm.mainBlock);

        if (!sym.retType.type.equals("VOIDTK") && elm.mainBlock.isWithoutReturn()) {
            errorList.add(new GramError(ErrorCode.g, elm.mainBlock.blockRLineNum));
        }

        currentBasicBlock = null;
        currentFunction = null;

        currentSymbolTable = currentSymbolTable.getPreTable();
    }

    public VisitResult visitMulExpNodeForDouble(MulExpNodeForDouble elm) {
        var rt = new VisitResult();
        var r1 = visitMulExpNode(elm.mulExp);
        var r2 = visitUnaryExpNode(elm.unaryExp);
        var val1 = r1.constVal;
        var val2 = r2.constVal;

        assert r1.expressionType.equals(r2.expressionType);
        rt.expressionType = r1.expressionType;

        if (val1 != null && val2 != null) {
            if (elm.op == TKType.MULT) {
                rt.constVal = val1 * val2;
            } else if (elm.op == TKType.DIV) {
                rt.constVal = val1 / val2;
            } else {
                rt.constVal = val1 % val2;
            }
        }
        if (currentBasicBlock != null) {
            if (elm.op == TKType.MULT) {
                rt.irValue = currentBasicBlock.createMulInst(r1.irValue, r2.irValue);
            } else if (elm.op == TKType.DIV) {
                rt.irValue = currentBasicBlock.createSDivInst(r1.irValue, r2.irValue);
            } else {
                rt.irValue = currentBasicBlock.createSRemInst(r1.irValue, r2.irValue);
            }
        }

        return rt;
    }

    public VisitResult visitMulExpNodeForSingle(MulExpNodeForSingle elm) {
        return visitUnaryExpNode(elm.unaryExp);
    }

    public VisitResult visitMulExpNode(MulExpNode elm) {
        if (elm instanceof MulExpNodeForDouble) {
            return visitMulExpNodeForDouble((MulExpNodeForDouble) elm);
        } else {
            return visitMulExpNodeForSingle((MulExpNodeForSingle) elm);
        }
    }

    public VisitResult visitNumberNode(NumberNode elm) {
        var rt = new VisitResult();
        rt.expressionType.type = "INTTK";
        rt.constVal = Integer.parseInt(elm.intConst);
        rt.irValue = new ImmediateValue(rt.constVal);
        return rt;
    }

    public VisitResult visitCharacterNode(CharacterNode elm) {
        var rt = new VisitResult();
        rt.expressionType.type = "CHARTK";
//        rt.constVal = (int) elm.charConst.charAt(1);
        rt.constVal = elm.getCharConstValue();
        rt.irValue = new ImmediateValue(rt.constVal);
        return rt;
    }


    public VisitResult visitPrimaryExpNodeForExp(PrimaryExpNodeForExp elm) {
        return visitExpNode(elm.exp);
    }

    public VisitResult visitPrimaryExpNodeForLVal(PrimaryExpNodeForLVal elm) {
        var r = visitLValNode(elm.lVal);
        if (!Objects.equals(r.expressionType.type, "ERR") && currentBasicBlock != null) {
            if (r.lvalLoadNotNeed) {
                return r;
            }
            r.irValue = currentBasicBlock.createLoadInst(r.irValue);
        }
        return r;
    }

    public VisitResult visitPrimaryExpNodeForNumber(PrimaryExpNodeForNumber elm) {
        return visitNumberNode(elm.number);
    }

    public VisitResult visitPrimaryExpNodeForCharacter(PrimaryExpNodeForCharacter elm) {
        return visitCharacterNode(elm.character);
    }

    public VisitResult visitPrimaryExpNode(PrimaryExpNode elm) {
        if (elm instanceof PrimaryExpNodeForExp) {
            return visitPrimaryExpNodeForExp((PrimaryExpNodeForExp) elm);
        } else if (elm instanceof PrimaryExpNodeForLVal) {
            return visitPrimaryExpNodeForLVal((PrimaryExpNodeForLVal) elm);
        } else if (elm instanceof PrimaryExpNodeForCharacter) {
            return visitPrimaryExpNodeForCharacter((PrimaryExpNodeForCharacter) elm);
        } else {
            return visitPrimaryExpNodeForNumber((PrimaryExpNodeForNumber) elm);
        }
    }

    public VisitResult visitRelExpNodeForDouble(RelExpNodeForDouble elm) {
        var rt = new VisitResult();
        var r1 = visitRelExpNode(elm.relExp);
        var r2 = visitAddExpNode(elm.addExp);

        if (r1.irValue instanceof ICmpInstruction) {
            r1.irValue = currentBasicBlock.createZExtInst(IRType.getInt(), r1.irValue);
        }
        if (r2.irValue instanceof ICmpInstruction) {
            r2.irValue = currentBasicBlock.createZExtInst(IRType.getInt(), r2.irValue);
        }

        CmpType cond = switch (elm.op) {
            case LSS -> CmpType.SLT;
            case GRE -> CmpType.SGT;
            case LEQ -> CmpType.SLE;
            case GEQ -> CmpType.SGE;
            default -> null; // impossible
        };

        rt.irValue = currentBasicBlock.createICmpInst(cond, r1.irValue, r2.irValue);
        return rt;
    }

    public VisitResult visitRelExpNodeForSingle(RelExpNodeForSingle elm) {
        return visitAddExpNode(elm.addExp);
    }

    public VisitResult visitRelExpNode(RelExpNode elm) {
        if (elm instanceof RelExpNodeForDouble) {
            return visitRelExpNodeForDouble((RelExpNodeForDouble) elm);
        } else {
            return visitRelExpNodeForSingle((RelExpNodeForSingle) elm);
        }
    }

    public void visitStmtNodeForAssign(StmtNodeForAssign elm) {

        var rtLVal = visitLValNode(elm.lVal);
        var lValSym = currentSymbolTable.getSymbol(elm.lVal.ident);
        if (lValSym == null) return;
        if (lValSym instanceof VarSymbol lValVarSym && lValVarSym.isConst) {
            errorList.add(new GramError(ErrorCode.h, elm.lVal.identLineNum));
        }

        var rtExp = visitExpNode(elm.exp);

        if ((lValSym.getIdentType().startsWith("Char"))) {
            rtExp.irValue = currentBasicBlock.createAndInst(rtExp.irValue, new ImmediateValue(0xFF));
        }
        currentBasicBlock.createStoreInst(rtExp.irValue, rtLVal.irValue);
    }

    public void visitStmtNodeForBlock(StmtNodeForBlock elm) {
        currentSymbolTable = currentSymbolTable.createSubTable();

        visitBlockNode(elm.block);
        currentSymbolTable = currentSymbolTable.getPreTable();
    }

    public void visitStmtNodeForContinueBreak(StmtNodeForContinueBreak elm) {
        if (loopDepth == 0) {
            errorList.add(new GramError(ErrorCode.m, elm.tkLineNum));
            return;
        }

        if (elm.type == TKType.CONTINUETK) {
            continueBranches.peek().add((BrInstruction) currentBasicBlock.createBrInstWithoutCond(null));
            currentBasicBlock = currentFunction.createBasicBlock();
            currentBasicBlock.setLoopNum(loopDepth);

        } else if (elm.type == TKType.BREAKTK) {
            breakBranches.peek().add((BrInstruction) currentBasicBlock.createBrInstWithoutCond(null));
            currentBasicBlock = currentFunction.createBasicBlock();
            currentBasicBlock.setLoopNum(loopDepth);

        }
    }

    public void visitStmtNodeForExp(StmtNodeForExp elm) {
        if (elm.exp != null) {
            visitExpNode(elm.exp);
        }
    }

    public void visitStmtNodeForGetInt(StmtNodeForGetInt elm) {
        var r = visitLValNode(elm.lVal);
        var lValSym = currentSymbolTable.getSymbol(elm.lVal.ident);
        if (lValSym instanceof VarSymbol lValVarSym && lValVarSym.isConst) {
            errorList.add(new GramError(ErrorCode.h, elm.lVal.identLineNum));
        }
        var getIntVal = currentBasicBlock.createCallInst(FunctionIRType.BUILD_IN_GETINT, List.of());
        currentBasicBlock.createStoreInst(getIntVal, r.irValue);
    }

    public void visitStmtNodeForGetChar(StmtNodeForGetChar elm) {
        var r = visitLValNode(elm.lVal);
        var lValSym = currentSymbolTable.getSymbol(elm.lVal.ident);
        if (lValSym instanceof VarSymbol lValVarSym && lValVarSym.isConst) {
            errorList.add(new GramError(ErrorCode.h, elm.lVal.identLineNum));
        }
        var getIntVal = currentBasicBlock.createCallInst(FunctionIRType.BUILD_IN_GETCHAR, List.of());
        currentBasicBlock.createStoreInst(getIntVal, r.irValue);
    }

    public void visitStmtNodeForIfElse(StmtNodeForIfElse elm) {
        var r = visitCondNode(elm.cond);

        var trueBlock = currentBasicBlock;
        visitStmtNode(elm.ifStmt);

        var lastBlockInTrue = currentBasicBlock;
        currentBasicBlock = currentFunction.createBasicBlock();
        currentBasicBlock.setLoopNum(loopDepth);

        var falseBlock = currentBasicBlock;

        if (elm.elseStmt != null) {
            visitStmtNode(elm.elseStmt);
            var lastBlockInFalse = currentBasicBlock;
            currentBasicBlock = currentFunction.createBasicBlock();
            currentBasicBlock.setLoopNum(loopDepth);

            lastBlockInFalse.createBrInstWithoutCond(currentBasicBlock);
        }
        lastBlockInTrue.createBrInstWithoutCond(currentBasicBlock);

//        for (var blockToTrue : r.trueConditionBlocks) {
//            var brInst = (BrInst) blockToTrue.getInstructions().get(blockToTrue.getInstructions().size() - 1);
//            brInst.setTrueBranch(trueBlock);
//        }
//
//        for (var blockToFalse : r.falseConditionBlocks) {
//            var brInst = (BrInst) blockToFalse.getInstructions().get(blockToFalse.getInstructions().size() - 1);
//            brInst.setFalseBranch(falseBlock);
//        }
        r.trueConditionBlocks.forEach(blockToTrue -> {
            var brInst = (BrInstruction) blockToTrue.getInstructions().get(blockToTrue.getInstructions().size() - 1);
            brInst.setTrueBranch(trueBlock);
        });

        r.falseConditionBlocks.forEach(blockToFalse -> {
            var brInst = (BrInstruction) blockToFalse.getInstructions().get(blockToFalse.getInstructions().size() - 1);
            brInst.setFalseBranch(falseBlock);
        });
    }

    public void visitStmtNodeForLoop(StmtNodeForLoop elm) {
        breakBranches.push(new ArrayList<>());
        continueBranches.push(new ArrayList<>());

        var forStmt1Block = currentBasicBlock;
        if (elm.forStmt1 != null) {
            visitForStmtNode(elm.forStmt1);
        }

        currentBasicBlock = currentFunction.createBasicBlock();
        currentBasicBlock.setLoopNum(loopDepth);

        forStmt1Block.createBrInstWithoutCond(currentBasicBlock);
        var loopEntryBlock = currentBasicBlock;

        var condRt = new VisitResult();
        if (elm.cond != null) {
            condRt = visitCondNode(elm.cond);
        }
        var stmtBlock = currentBasicBlock;

        loopDepth++;
        visitStmtNode(elm.stmt);
        loopDepth--;

        var lastBlockInStmt = currentBasicBlock;
        currentBasicBlock = currentFunction.createBasicBlock();
        currentBasicBlock.setLoopNum(loopDepth);

        lastBlockInStmt.createBrInstWithoutCond(currentBasicBlock);
        var forStmt2Block = currentBasicBlock;

        if (elm.forStmt2 != null) {
            visitForStmtNode(elm.forStmt2);
        }
        forStmt2Block.createBrInstWithoutCond(loopEntryBlock);

        currentBasicBlock = currentFunction.createBasicBlock();
        currentBasicBlock.setLoopNum(loopDepth);

        var loopExitBlock = currentBasicBlock;

        for (var blockToTrue : condRt.trueConditionBlocks) {
            var brInst = (BrInstruction) blockToTrue.getInstructions().get(blockToTrue.getInstructions().size() - 1);
            brInst.setTrueBranch(stmtBlock);
        }

        for (var blockToFalse : condRt.falseConditionBlocks) {
            var brInst = (BrInstruction) blockToFalse.getInstructions().get(blockToFalse.getInstructions().size() - 1);
            brInst.setFalseBranch(loopExitBlock);
        }

        for (var continueBrInst : continueBranches.pop()) {
            continueBrInst.setDest(forStmt2Block);
        }

        for (var breakBrInst : breakBranches.pop()) {
            breakBrInst.setDest(loopExitBlock);
        }
    }

    public void visitStmtNodeForPrintf(StmtNodeForPrintf elm) {
        List<Value> expValues = new ArrayList<>();
        for (var exp : elm.exps) {
            expValues.add(visitExpNode(exp).irValue);
        }

        var fCharNum = (elm.StringConst.length() - String.join("", elm.StringConst.split("%d|%s|%c")).length()) / 2;
        if (fCharNum != elm.exps.size()) {
            errorList.add(new GramError(ErrorCode.l, elm.printfLineNum));
            return;
        }

        try {
            for (int i = 1, j = 0; i < elm.StringConst.length() - 1; i++) {
                char ch = elm.StringConst.charAt(i);
                if (ch == '%') {
                    char nextChar = elm.StringConst.charAt(i + 1);
                    if (nextChar == 'd') {
                        currentBasicBlock.createCallInst(FunctionIRType.BUILD_IN_PUTINT, List.of((expValues.get(j) == null ? new ImmediateValue(0) : expValues.get(j++))));
                    } else if (nextChar == 's') {
                        currentBasicBlock.createCallInst(FunctionIRType.BUILD_IN_PUTSTR, List.of((expValues.get(j) == null ? new ImmediateValue(0) : expValues.get(j++))));
                    } else if (nextChar == 'c') {
                        currentBasicBlock.createCallInst(FunctionIRType.BUILD_IN_PUTCH, List.of((expValues.get(j) == null ? new ImmediateValue(0) : expValues.get(j++))));
                    }
                    i++;
                } else if (ch == '\\') {
                    currentBasicBlock.createCallInst(FunctionIRType.BUILD_IN_PUTCH, List.of(new ImmediateValue('\n')));
                    i++;
                } else {
                    currentBasicBlock.createCallInst(FunctionIRType.BUILD_IN_PUTCH, List.of(new ImmediateValue(ch)));
                }
            }
        } catch (IndexOutOfBoundsException e) {
//            return; // exception means that the format string is illegal
        }
    }

    public void visitStmtNodeForReturn(StmtNodeForReturn elm) {
        Value expIr = null;
        if (elm.exp != null) {
            if (isReturnExpressionNotNeeded) {
                errorList.add(new GramError(ErrorCode.f, elm.returnLineNum));
                return;
            }
            var result = visitExpNode(elm.exp);
            expIr = result.irValue;
        }
        currentBasicBlock.createReturnInst(expIr);
    }

    public void visitStmtNode(StmtNode elm) {
        if (elm instanceof StmtNodeForAssign e) {
            visitStmtNodeForAssign(e);
        } else if (elm instanceof StmtNodeForBlock e) {
            visitStmtNodeForBlock(e);
        } else if (elm instanceof StmtNodeForContinueBreak e) {
            visitStmtNodeForContinueBreak(e);
        } else if (elm instanceof StmtNodeForExp e) {
            visitStmtNodeForExp(e);
        } else if (elm instanceof StmtNodeForGetInt e) {
            visitStmtNodeForGetInt(e);
        } else if (elm instanceof StmtNodeForGetChar e) {
            visitStmtNodeForGetChar(e);
        } else if (elm instanceof StmtNodeForIfElse e) {
            visitStmtNodeForIfElse(e);
        } else if (elm instanceof StmtNodeForLoop e) {
            visitStmtNodeForLoop(e);
        } else if (elm instanceof StmtNodeForPrintf e) {
            visitStmtNodeForPrintf(e);
        } else {
            visitStmtNodeForReturn((StmtNodeForReturn) elm);
        }
    }

    public VisitResult visitUnaryExpNodeForFuncCall(UnaryExpNodeForFuncCall elm) {
        var rt = new VisitResult();
        var sym = currentSymbolTable.getSymbol(elm.ident);
        if (sym == null) {//未定义的符号5
            errorList.add(new GramError(ErrorCode.c, elm.identLineNum));
            rt.expressionType.type = "ERR"; // maybe wrong
            rt.irValue = new ImmediateValue(0);
            rt.constVal = 0;
            rt.irValues = List.of(new ImmediateValue(0));
            rt.constantInitValues = List.of(0);
            rt.parameterTypes = List.of(new Type());
            return rt;
        }
        FunctionSymbol funcSym = (FunctionSymbol) sym;
        rt.expressionType = funcSym.retType;
        if (elm.params != null) {
            var r = visitFuncRParamsNode(elm.params);

            if (elm.params.exps.size() != funcSym.paramTypeList.size()) {
                errorList.add(new GramError(ErrorCode.d, elm.identLineNum));
                return rt;
            }

            for (int i = 0; i < funcSym.paramTypeList.size(); i++) {
                if (!funcSym.paramTypeList.get(i).equals(r.parameterTypes.get(i))) {
                    if (!funcSym.paramTypeList.get(i).dims.isEmpty() || !r.parameterTypes.get(i).dims.isEmpty()) {//e型错误-至少有一个是数组类型
                        errorList.add(new GramError(ErrorCode.e, elm.identLineNum));
                        break;
                    }
                }
            }
            rt.irValue = currentBasicBlock.createCallInst((FunctionIRType) funcSym.targetValue, r.irValues);
        } else {
            if (!funcSym.paramTypeList.isEmpty()) {
                errorList.add(new GramError(ErrorCode.d, elm.identLineNum));
                return rt;
            }
            rt.irValue = currentBasicBlock.createCallInst((FunctionIRType) funcSym.targetValue, List.of());
        }
        return rt;
    }

    public VisitResult visitUnaryExpNodeForPrimaryExp(UnaryExpNodeForPrimaryExp elm) {
        return visitPrimaryExpNode(elm.primaryExp);
    }

    //    public VisitResult visitUnaryExpNodeForUnaryOp(UnaryExpNodeForUnaryOp elm) {
//        var rt = new VisitResult();
//        var r = visitUnaryExpNode(elm.exp);
//        var val = r.constVal;
//
//        assert r.expressionType != null;
//        rt.expressionType = r.expressionType;
//
//        if (val != null) {
//            var op = visitUnaryOpNode(elm.op);
//            if (op == TKType.MINU) {
//                rt.constVal = -val;
//            } else if (op == TKType.PLUS) {
//                rt.constVal = val;
//            } else {
//                rt.constVal = val == 0 ? 0 : 1;
//            }
//        }
//        if (currentBasicBlock != null) {
//            var op = visitUnaryOpNode(elm.op);
//            if (op == TKType.MINU) {
//                rt.irValue = currentBasicBlock.createSubInst(new ImmediateValue(0), r.irValue);
//            } else if (op == TKType.PLUS) {
//                rt.irValue = r.irValue; // if op is +, do nothing
//            } else {
//                rt.irValue = currentBasicBlock.createICmpInst(ICmpInstCond.EQ, new ImmediateValue(0), r.irValue);
//            }
//        }
//        return rt;
//    }
    public VisitResult visitUnaryExpNodeForUnaryOp(UnaryExpNodeForUnaryOp elm) {
        var rt = new VisitResult();
        var r = visitUnaryExpNode(elm.exp);
        var val = r.constVal;

//    assert r.expressionType != null;
        rt.expressionType = r.expressionType;

        if (val != null) {
            var op = visitUnaryOpNode(elm.op);
            rt.constVal = switch (op) {
                case MINU -> -val;
                case PLUS -> val;
                default -> val == 0 ? 0 : 1;
            };
        }
        if (currentBasicBlock != null) {
            var op = visitUnaryOpNode(elm.op);
            rt.irValue = switch (op) {
                case MINU -> currentBasicBlock.createSubInst(new ImmediateValue(0), r.irValue);
                case PLUS -> r.irValue; // if op is +, do nothing
                default -> currentBasicBlock.createICmpInst(CmpType.EQ, new ImmediateValue(0), r.irValue);
            };
        }

        return rt;
    }

    public VisitResult visitUnaryExpNode(UnaryExpNode elm) {
        if (elm instanceof UnaryExpNodeForFuncCall) {
            return visitUnaryExpNodeForFuncCall((UnaryExpNodeForFuncCall) elm);
        } else if (elm instanceof UnaryExpNodeForPrimaryExp) {
            return visitUnaryExpNodeForPrimaryExp((UnaryExpNodeForPrimaryExp) elm);
        } else {
            return visitUnaryExpNodeForUnaryOp((UnaryExpNodeForUnaryOp) elm);
        }
    }

    public TKType visitUnaryOpNode(UnaryOpNode elm) {
        return elm.opType;
    }

    public void visitVarDeclNode(VarDeclNode elm) {
        visitBTypeNode(elm.type);
//        for (var varDef : elm.varDefs) {
//            visitVarDefNode(elm.type, varDef);
//        }
        elm.varDefs.forEach(varDef -> visitVarDefNode(elm.type, varDef));
    }

//    public void visitVarDefNode(BTypeNode BType, VarDefNode elm) {
//        if (currentSymbolTable.contains(elm.ident)) {
//            errorList.add(new GramError(ErrorCode.b, elm.identLineNum));
//        }
//
//        var varSym = new VarSymbol();
//        varSym.isConst = false;
//        varSym.ident = elm.ident;
//        varSym.varType.type = BType.type.getTktype();
//        for (var dim : elm.dimensions) {
//            varSym.varType.dims.add(visitConstExpNode(dim).constVal);
//        }
//
//        if (isGlobalVar) {//全局变量
////        if (true) {
//            if (elm.initVal != null) {
//                var r = visitInitValNode(elm.initVal);
//                varSym.values.addAll(r.constantInitValues);
//            }
//            while (!varSym.varType.dims.isEmpty() && varSym.values.size() < varSym.varType.dims.get(0)) {
//                varSym.values.add(0);
//            }//补全0
//            var globalVar = irModule.createGlobalValue(IRType.getInt().dims(varSym.varType.dims), varSym.values);
//            globalVar.setName(varSym.ident);
//            varSym.targetValue = globalVar;
//        } else {
//            var localVar = currentFunction.getFirstBasicBlock().createAllocaInstAndInsertToFront(IRType.getInt().dims(varSym.varType.dims));
//            varSym.targetValue = localVar;
//            if (elm.initVal != null) {
//                var r = visitInitValNode(elm.initVal);
//                var initValues = r.irValues;
//                if (!varSym.isArray()) {
//                    currentBasicBlock.createStoreInst(r.irValues.get(0), varSym.targetValue);
//                } else { // array with init values
//                    for (int i = 0; i < initValues.size(); i++) {
//                        int[] idxs = new int[varSym.varType.dims.size()];
//                        var pos = i;
//                        for (int j = idxs.length - 1; j >= 0; j--) {
//                            idxs[j] = pos % varSym.varType.dims.get(j);
//                            pos /= varSym.varType.dims.get(j);
//                        }
//
//                        var initVal = initValues.get(i);
//                        var arrayPtr = currentBasicBlock.createGetElementPtrInst(varSym.targetValue, List.of(new ImmediateValue(0), new ImmediateValue(0)));
//                        for (int j = 0; j < idxs.length; j++) {
//                            var visitIdx = idxs[j];
//                            List<Value> offsets = j == idxs.length - 1 ? List.of(new ImmediateValue(visitIdx)) : List.of(new ImmediateValue(visitIdx), new ImmediateValue(0));
//                            arrayPtr = currentBasicBlock.createGetElementPtrInst(arrayPtr, offsets);
//                        }
//                        currentBasicBlock.createStoreInst(initVal, arrayPtr);
//                    }
//                }
//            }
//        }
//        currentSymbolTable.insertSymbol(varSym);
//    }

    public void visitVarDefNode(BTypeNode BType, VarDefNode elm) {
        if (currentSymbolTable.contains(elm.ident)) {
            errorList.add(new GramError(ErrorCode.b, elm.identLineNum));
        }

        var varSym = new VarSymbol();
        varSym.isConst = false;
        varSym.ident = elm.ident;
        varSym.varType.type = BType.type.getTktype();
        for (var dim : elm.dimensions) {
            varSym.varType.dims.add(visitConstExpNode(dim).constVal);
        }

        if (isGlobalVar) {
            handleGlobalVar(varSym, elm);
        } else {
            handleLocalVar(varSym, elm);
        }

        currentSymbolTable.insertSymbol(varSym);
    }

    private void handleGlobalVar(VarSymbol varSym, VarDefNode elm) {
        if (elm.initVal != null) {
            var r = visitInitValNode(elm.initVal);
            varSym.values.addAll(r.constantInitValues);
        }
        while (!varSym.varType.dims.isEmpty() && varSym.values.size() < varSym.varType.dims.get(0)) {
            varSym.values.add(0);
        }
        var globalVar = irModule.createGlobalValue(IRType.getInt().dims(varSym.varType.dims), varSym.values);
        globalVar.setName(varSym.ident);
        varSym.targetValue = globalVar;
    }

    private void handleLocalVar(VarSymbol varSym, VarDefNode elm) {
        varSym.targetValue = currentFunction.getFirstBasicBlock().createAllocaInstAndInsertToFront(IRType.getInt().dims(varSym.varType.dims));

        if (elm.initVal != null) {
            var r = visitInitValNode(elm.initVal);
            var initValues = r.irValues;
            if (!varSym.isArray()) {
                currentBasicBlock.createStoreInst(r.irValues.get(0), varSym.targetValue);
            } else {
                for (int i = 0; i < initValues.size(); i++) {
                    int[] idxs = new int[varSym.varType.dims.size()];
                    var pos = i;
                    for (int j = idxs.length - 1; j >= 0; j--) {
                        idxs[j] = pos % varSym.varType.dims.get(j);
                        pos /= varSym.varType.dims.get(j);
                    }

                    var initVal = initValues.get(i);
                    var arrayPtr = currentBasicBlock.createGetElementPtrInst(varSym.targetValue, List.of(new ImmediateValue(0), new ImmediateValue(0)));
                    for (int j = 0; j < idxs.length; j++) {
                        var visitIdx = idxs[j];
                        List<Value> offsets = j == idxs.length - 1 ? List.of(new ImmediateValue(visitIdx)) : List.of(new ImmediateValue(visitIdx), new ImmediateValue(0));
                        arrayPtr = currentBasicBlock.createGetElementPtrInst(arrayPtr, offsets);
                    }
                    currentBasicBlock.createStoreInst(initVal, arrayPtr);
                }
            }
        }
    }
}
